/**
 * 手写 Promise 源码
 * 尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理
 */

/**
 * Promise 有三种状态
 * pending：等待
 * fulfilled：成功
 * rejected：失败
 * 状态变更只能：
 * resolve：pending -> fulfilled
 * reject：pending -> rejected
 * 状态一旦确定就不可更改
 */
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败

// Promise 就是 一个类
class MyPromise {
  // Promise 类，需要一个 执行器，执行器内部是立即同步执行的
  constructor(executor) {
    // 同步执行过程中，会捕获异常，作为 Promise 失败结果 的 原因
    try {
      /**
       * 执行器有两个参数，resolve，reject，更改状态并保存 Promise 成功结果 || 失败原因
       * resolve：pending -> fulfilled
       * reject：pending -> rejected
       * 状态一旦确定就不可更改
       */
      executor(this.resolve, this.reject);
    } catch (e) {
      this.reject(e);
    }
  }

  // 初始化
  status = PENDING; // 状态
  value = undefined; // 成功结果值
  reason = undefined; // 失败结果原因
  successCallback = []; // 暂存等待状态时的成功回调
  failCallback = []; // 暂存等待状态时的失败回调

  resolve = (value) => {
    // 状态成功或失败后，不可再更改，即：如果不是等待状态，阻止程序继续执行
    if (this.status !== PENDING) return;

    // 变更状态 为 成功
    this.status = FULFILLED;

    // 保存 成功结果值
    this.value = value;

    // 判断成功回调是否存在，存在就弹出调用
    while (this.successCallback.length) this.successCallback.shift()();
  };

  reject = (reason) => {
    // 状态成功或失败后，不可再更改，即：如果不是等待状态，阻止程序继续执行
    if (this.status !== PENDING) return;

    // 变更状态 为 失败
    this.status = REJECTED;

    // 保存 失败结果原因
    this.reason = reason;

    // 判断失败回调是否存在，存在就弹出调用
    while (this.failCallback.length) this.failCallback.shift()();
  };

  /**
   * 通过状态判断，执行相应的回调，拿到 Promise 结果，并返回 全新 的 Promise 对象
   * 可 多次、链式 调用，前一个 then 的输出，作为 后一个 then 的输入 【管道】
   */
  then = (successCallback, failCallback) => {
    // 参数为 函数，可选，不是函数就无视它
    successCallback =
      successCallback && typeof successCallback === "function"
        ? successCallback
        : (value) => value;
    failCallback =
      failCallback && typeof failCallback === "function"
        ? failCallback
        : (reason) => {
            throw reason;
          };

    // then 会返回一个全新的 Promise 对象
    let promise = new MyPromise((resolve, reject) => {
      // 判断状态，执行相应的回调
      /**
       * 因为执行器内部是立即同步执行，而 Promise 规定：不能将当前 Promise 作为 回调结果返回
       * 所以这里声明并最后返回的 promise，会被用来和当前 Promise 对象作比较
       * 故：需要异步延迟执行才能拿到新生成的要作为结果返回的 Promise 对象(promise)
       */
      setTimeout(() => {
        if (this.status === FULFILLED) {
          // 成功
          // 执行过程中会捕获异常，直接作为 Promise 失败结果 的 原因
          try {
            // 成功回调的结果
            let x = successCallback(this.value);
            /**
             * 需要做的事：
             * 判断 成功回调的结果 是否是 当前 Promise 对象自身
             * 如果是，直接抛出 TypeError("Chaining cycle detected for promise #<Promise>") 异常，作为 Promise 失败结果 的 原因
             * 判断 x 是普通值还是 Promise 对象
             * 如果是普通值，直接调用 resolve
             * 如果是 Promise 对象，调用 then, 查看 Promise 对象返回的结果
             * 再根据 Promise 对象返回的结果决定调用 resolve, 还是 reject
             */
            resolvePromise(promise, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        } else if (this.status === REJECTED) {
          // 失败
          // 执行过程中会捕获异常，直接作为 Promise 失败结果 的 原因
          try {
            // 失败回调的结果
            let x = failCallback(this.reason);
            /**
             * 需要做的事：
             * 判断 失败回调的结果 是否是 当前 Promise 对象自身
             * 如果是，直接抛出 TypeError("Chaining cycle detected for promise #<Promise>") 异常，作为 Promise 失败结果 的 原因
             * 判断 x 是普通值还是 Promise 对象
             * 如果是普通值，直接调用 resolve
             * 如果是 Promise 对象，调用 then, 查看 Promise 对象返回的结果
             * 再根据 Promise 对象返回的结果决定调用 resolve, 还是 reject
             */
            resolvePromise(promise, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        } else {
          // 等待，暂存 成功回调 和 失败回调
          this.successCallback.push(() => {
            // 执行过程中会捕获异常，直接作为 Promise 失败结果 的 原因
            try {
              // 成功回调的结果
              let x = successCallback(this.value);
              /**
               * 需要做的事：
               * 判断 成功回调的结果 是否是 当前 Promise 对象自身
               * 如果是，直接抛出 TypeError("Chaining cycle detected for promise #<Promise>") 异常，作为 Promise 失败结果 的 原因
               * 判断 x 是普通值还是 Promise 对象
               * 如果是普通值，直接调用 resolve
               * 如果是 Promise 对象，调用 then, 查看 Promise 对象返回的结果
               * 再根据 Promise 对象返回的结果决定调用 resolve, 还是 reject
               */
              resolvePromise(promise, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          });
          this.failCallback.push(() => {
            // 执行过程中会捕获异常，直接作为 Promise 失败结果 的 原因
            try {
              // 失败回调的结果
              let x = failCallback(this.reason);
              /**
               * 需要做的事：
               * 判断 失败回调的结果 是否是 当前 Promise 对象自身
               * 如果是，直接抛出 TypeError("Chaining cycle detected for promise #<Promise>") 异常，作为 Promise 失败结果 的 原因
               * 判断 x 是普通值还是 Promise 对象
               * 如果是普通值，直接调用 resolve
               * 如果是 Promise 对象，调用 then, 查看 Promise 对象返回的结果
               * 再根据 Promise 对象返回的结果决定调用 resolve, 还是 reject
               */
              resolvePromise(promise, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          });
        }
      }, 0);
    });
    return promise;
  };

  /**
   * resolve, 静态成员
   * 将值转成 Promise 对象 返回
   */
  static resolve = (value) => {
    if (value instanceof MyPromise) return value;
    return new MyPromise((resolve) => resolve(value));
  };

  /**
   * all, 静态成员
   * 参数为数组, 数组中可以传递 普通值 和 Promise 对象
   * 解决异步并发、允许按照异步API调用顺序得到异步API的执行结果
   */
  static all = (array) => {
    let result = []; // 成功状态的结果
    let index = 0;
    return new MyPromise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value;
        index++;
        // 所有调用完成，才更改状态为 成功，并返回当前 Promise 的成功结果
        if (index === array.length) {
          resolve(result);
        }
      }
      for (let i = 0; i < array.length; i++) {
        const current = array[i];
        if (current instanceof MyPromise) {
          // Promise 对象
          current.then(
            (value) => addData(i, value),
            (reason) => reject(reason) // 只要有一个状态为 失败，直接返回当前 Promise 失败结果 的 原因
          );
        } else {
          // 普通值
          addData(i, array[i]);
        }
      }
    });
  };

  /**
   * 无论当前的 Promise 对象状态是成功的还是失败的, finally 方法中传入的回调函数都会执行
   * 返回值：全新的 Promise 对象，所以可链式调用 then 方法获取到 Promise 对象的执行结果
   */
  finally = (callback) => {
    return this.then(
      (value) => MyPromise.resolve(callback()).then(() => value),
      (reason) =>
        MyPromise.resolve(callback()).then(() => {
          throw reason;
        })
    );
  };

  /**
   * 当前的 Promise 对象状态为失败时，失败回调被执行
   * 相当于 直接调用 then 的失败回调函数
   * 返回值：全新的 Promise 对象，故：也可链式调用 then 得到 Promise 对象失败结果的原因
   */
  catch = (failCallback) => this.then(undefined, failCallback);
}

/**
 * 需要做的事：
 * 判断 成功回调的结果 是否是 当前 Promise 对象自身
 * 如果是，直接抛出 TypeError("Chaining cycle detected for promise #<Promise>") 异常，作为 Promise 失败结果 的 原因
 * 判断 x 是普通值还是 Promise 对象
 * 如果是普通值，直接调用 resolve
 * 如果是 Promise 对象，调用 then, 查看 Promise 对象返回的结果
 * 再根据 Promise 对象返回的结果决定调用 resolve, 还是 reject
 * promise: then 最终返回的全新的 Promise 对象
 * x: 成功回调的结果
 */
function resolvePromise(promise, x, resolve, reject) {
  if (promise === x) {
    return reject(
      new TypeError("Chaining cycle detected for promise #<Promise>")
    );
  }
  if (x instanceof MyPromise) {
    // Promise 对象
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}

module.exports = MyPromise;
